package com.zlm.custom.mybatis;

import java.lang.reflect.*;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author zlm
 * @date 2025/4/22
 */
public class MySqlSessionFactory {

    private static final String JDBCURL = "jdbc:mysql://localhost:3306/mybatis";
    private static final String USERNAME = "root";
    private static final String PASSWORD = "root";
    @SuppressWarnings("all")
    public <T>  T getMapper(Class<T> clazz){
        return (T)Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{clazz}, new MapperInvocationHandler());
    }

    static class MapperInvocationHandler implements InvocationHandler{
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if(method.getName().startsWith("select")){
                return invokeSelect(proxy,method,args);
            }
            return null;
        }

        private Object invokeSelect(Object proxy, Method method, Object[] args) {

            String sql = createSelectSql(proxy,method,args);


            try(Connection connection = DriverManager.getConnection(JDBCURL, USERNAME, PASSWORD)) {
                PreparedStatement statement = connection.prepareStatement(sql);
                for (int i = 0; i < args.length; i++) {
                    if(args[i] instanceof Integer){
                        statement.setInt(i+1, (Integer) args[i]);
                    }else if(args[i] instanceof String){
                        statement.setString(i+1, (String) args[i]);
                    }
                }

                ResultSet rs = statement.executeQuery();
                while (rs.next()) {
                    return parseResult(rs, method.getReturnType());

                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }

        private Object parseResult(ResultSet rs, Class<?> returnType) throws Exception {
            //无参构造
            Constructor<?> constructor = returnType.getConstructor();
            //获取返回对象
            Object result = constructor.newInstance();
            //所有属性
            Field[] declaredFields = returnType.getDeclaredFields();
            for (Field declaredField : declaredFields) {
                //字段属性名
                String name = declaredField.getName();
                Object value = null;
                //字段类型
                Class<?> type = declaredField.getType();
                if(type == Integer.class){
                    value = rs.getInt(name);
                }else if(type == String.class){
                    value = rs.getString(name);
                }
                declaredField.setAccessible(true);
                declaredField.set(result,value);

            }
            return result;

        }

        private String createSelectSql(Object proxy, Method method, Object[] args) {
            List<String> selectCols = getSelectCols(method.getReturnType());
            String tableName = getSelectTableName(method.getReturnType());
            String where = getSelectWhere(method);
            StringBuilder sqlBuilder = new StringBuilder();
            sqlBuilder.append("select ")
                    .append(String.join(",",selectCols))
                    .append(" from ")
                    .append(tableName)
                    .append(" where ")
                    .append(where);
            return sqlBuilder.toString();
        }

        private String getSelectWhere(Method method) {
            return Arrays.stream(method.getParameters())
                    .map( parameter ->{
                        Param param = parameter.getAnnotation(Param.class);
                        String column = param.value();
                        return column + " = ?";
                    }).collect(Collectors.joining(" and "));

        }

        private String getSelectTableName(Class<?> returnType) {
            Table table = returnType.getAnnotation(Table.class);
            if(table == null){
                throw new RuntimeException("返回值无法确定查询表");
            }

            return table.tableName();
        }

        private List<String> getSelectCols(Class<?> returnType) {
            Field[] declaredFields = returnType.getDeclaredFields();
            return Arrays.stream(declaredFields).map(Field::getName).collect(Collectors.toList());
        }
    }
}
